package xyz.xiezc.xhttp;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import lombok.extern.slf4j.Slf4j;
import xyz.xiezc.ioc.annotation.Component;
import xyz.xiezc.xhttp.common.PathDealPO;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * @author wb-xzc291800
 * @date 2019/04/24 14:40
 */
@Slf4j
@Component
public class HttpServer implements HttpHandler {

    public final static Map<String, PathDealPO> pathDealPOMap = new ConcurrentHashMap<>();
    private final static TimedCache<String, Map<String, Object>> sessionCache = CacheUtil.newTimedCache(30 * 60 * 1000);

    /**
     * 启动统计的http服务
     */
    public void startHttpServer(int port, Executor executor) {
        try {
            com.sun.net.httpserver.HttpServer server = com.sun.net.httpserver.HttpServer.create(new InetSocketAddress(port), 0);
            server.setExecutor(executor);
            server.createContext("/", this);
            server.start();
            log.info(" xHttpServer start on  {} port", port);
        } catch (IOException e) {
            log.error("启动统计服务器出现问题", e);
        }
    }


    @Override
    public void handle(HttpExchange exchange) {
        String path = exchange.getRequestURI().getPath();
        String requestMethod = exchange.getRequestMethod();
        log.info("收到http请求, Path:{}, method:{}", path, requestMethod);
        PathDealPO pathDealPO = pathDealPOMap.get(path);
        if (pathDealPO == null) {
            sendResp(exchange, 404, "not found");
        }
        if (StrUtil.equalsIgnoreCase(requestMethod, pathDealPO.getMethod())) {
            if (requestMethod.equalsIgnoreCase("get")) {
                invokePathMethod(exchange, pathDealPO);
            } else if (requestMethod.equalsIgnoreCase("post")) {
                invokePathMethod(exchange, pathDealPO);
            } else {
                sendResp(exchange, 404, "not found");
            }
        }
    }

    /**
     * 反射调用业务方法
     *
     * @param exchange
     * @param pathDealPO
     */
    private void invokePathMethod(HttpExchange exchange, PathDealPO pathDealPO) {
        try {
            List handlObj = pathDealPO.getHandlObj();
            Optional any = handlObj.parallelStream().findAny();
            String query = exchange.getRequestURI().getQuery();
            Headers requestHeaders = exchange.getRequestHeaders();

            Object invoke = null;
            int parameterCount = pathDealPO.getHandler().getParameterCount();
            if (parameterCount == 0) {
                invoke = pathDealPO.getHandler().invoke(any.get());
            } else if (parameterCount == 1) {
                invoke = pathDealPO.getHandler().invoke(any.get(), formData2Dic(query));
            } else if (parameterCount == 2) {
                invoke = pathDealPO.getHandler().invoke(any.get(), formData2Dic(query), requestHeaders);
            } else if (parameterCount == 3) {
                invoke = pathDealPO.getHandler().invoke(any.get(), formData2Dic(query), requestHeaders, exchange.getRequestBody());
            }

            HttpCookie sessionId = getSessionId(requestHeaders, false);
            if (sessionId != null) {
                exchange.getResponseHeaders().add("set-cookie", sessionId.toString());
            }
            sendResp(exchange, 200, Objects.toString(invoke));
        } catch (Exception e) {
            log.error("", e);
            sendResp(exchange, 500,e.getMessage());
        }
    }

    public static Map<String, Object> getSession(Map<String, List<String>> headers) {
        HttpCookie sessionId = getSessionId(headers, true);
        Map<String, Object> session = sessionCache.get(sessionId.getValue());
        if (session == null) {
            session = new ConcurrentHashMap<>(1);
            sessionCache.put(sessionId.getValue(), session);
        }
        return session;
    }


    private static HttpCookie getSessionId(Map<String, List<String>> headers, boolean need) {
        List<String> list = headers.get("cookie");
        if (list == null) {
            list = new ArrayList<>();
            headers.put("cookie", list);
        }
        for (String str : list) {
            List<HttpCookie> parse = HttpCookie.parse(str);
            Optional<HttpCookie> jsessionid = parse.parallelStream()
                    .filter(httpCookie -> Objects.equals("JSESSIONID", httpCookie.getName())).findFirst();
            HttpCookie httpCookie = jsessionid.orElse(null);
            if (httpCookie != null) {
                return httpCookie;
            }
        }
        if (need) {
            String sessionId = IdUtil.fastSimpleUUID();
            HttpCookie httpCookie = new HttpCookie("JSESSIONID", sessionId);
            list.add(httpCookie.toString());
            return httpCookie;
        } else {
            return null;
        }
    }

    private void sendResp(HttpExchange exchange, int code, String resp) {
        try (OutputStream os = exchange.getResponseBody()) {
            byte[] utf8s = Objects.toString(resp,"null").getBytes("utf8");
            exchange.sendResponseHeaders(code, utf8s.length);
            // 设置响应头
            exchange.getResponseHeaders().add("Content-Type", "application/json; charset=UTF-8");
            os.write(utf8s);
        } catch (Exception e) {
            log.error("响应", e);
        }
    }

    private Map<String, String> formData2Dic(String formData) {
        Map<String, String> result = new HashMap<>();
        if (formData == null || formData.trim().length() == 0) {
            return result;
        }
        final String[] items = formData.split("&");
        Arrays.stream(items).forEach(item -> {
            final String[] keyAndVal = item.split("=");
            if (keyAndVal.length == 2) {
                try {
                    final String key = URLDecoder.decode(keyAndVal[0], "utf8");
                    final String val = URLDecoder.decode(keyAndVal[1], "utf8");
                    result.put(key, val);
                } catch (UnsupportedEncodingException e) {
                }
            }
        });
        return result;
    }
}
